package models

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/astaxie/beego/orm"
	"strings"
	"time"
)

//用户类型
const (
	USER_TYPE_COMMON = iota
	USER_TYPE_VIP
	USER_TYPE_EXPERT
)

//用户性别
const (
	SEX_TYPE_MAN = iota
	SEX_TYPE_WEMAN
	SEX_TYPE_OTHER
)

//签到基本单位
const SIGNIN_BASE_UNIT = 5

//用于解决时间字段无法过滤问题
type TimeFieldFilter struct {
	structInfo   interface{}
	filterFields []string
	fimeFields   []string
}

func findField(fields []string, field string) bool {
	for _, f := range fields {
		if strings.ToLower(f) == field {
			return true
		}
	}
	return false
}

func (this *TimeFieldFilter) MarshalJSON() ([]byte, error) {
	rlt, err := json.Marshal(this.structInfo)
	if err == nil && len(this.filterFields) != 0 {
		for _, field := range this.fimeFields {
			//xxx,"birth_day": "0001-01-01T00:00:00Z",xxx
			if !findField(this.filterFields, field) {
				if index := bytes.Index(rlt, []byte(`"`+field+`"`)); index != -1 {
					rlt1 := rlt[index:] //"birth_day": "0001-01-01T00:00:00Z",xxx
					index1 := bytes.IndexByte(rlt1, ',')
					if index1 != -1 {
						index1 = index1 + 1
					} else {
						index1 = bytes.IndexByte(rlt1, '}')
					}
					if index1 != -1 {
						rlt2 := rlt1[:index1]
						copy(rlt1, rlt1[index1:])
						rlt = rlt[:len(rlt)-len(rlt2)]
						if index = bytes.Index(rlt, []byte(",}")); index != -1 {
							rlt[index] = '}'
							rlt = rlt[:len(rlt)-1]
						}
					}
				}
			}
		}
	}
	return rlt, err
}

//用户信息表
//time: RFC3339格式->2015-04-22T10:08:38.5904442+08:00
type User struct {
	Id        int       `json:"-"`
	Name      *string   `orm:"null;unique;size(12)" json:"name,omitempty"`
	Phone     *string   `orm:"null;unique;size(15)" json:"phone,omitempty"`
	PassWd    *string   `orm:"null" json:"-"`
	Sex       *byte     `orm:"null" json:"sex,omitempty"`
	Signature *string   `orm:"null;size(100)" json:"signature,omitempty"`
	ImageId   *int      `orm:"default(-1)" json:"image_id,omitempty"`
	NickName  *string   `orm:"null" json:"nick_name,omitempty"`
	BirthDay  time.Time `orm:"null;type(date)" json:"birth_day,omitempty"`
	UserType  *byte     `orm:"default(0)" json:"user_type,omitempty"`
	Updated   time.Time `orm:"auto_now;type(datetime)" json:"updated,omitempty"`
	Created   time.Time `orm:"auto_now_add;type(datetime)" json:"created,omitempty"`
}

func (this *User) GetFilterData(filterFields []string) *TimeFieldFilter {
	return &TimeFieldFilter{this, filterFields,
		[]string{"birth_day", "updated", "created"}}
}

type UserExt struct {
	UserId     int       `orm:"pk" json:"-"`
	Longitude  *float64  `orm:"null;index;digits(12);decimals(6)" json:"longitude,omitempty"`
	Latitude   *float64  `orm:"null;index;digits(12);decimals(6)" json:"latitude,omitempty"`
	LoginTime  time.Time `orm:"null;type(datetime)" json:"login_time,omitempty"`
	SigninDate time.Time `orm:"null;type(date)" json:"signin_date,omitempty"`
	SigninDays *int16    `orm:"default(0)" json:"signin_days,omitempty"`
	SigninUnit *int16    `orm:"default(5)" json:"signin_unit,omitempty"`
	Integral   *int32    `orm:"default(0)" json:"integral,omitempty"`
}

func (this *UserExt) GetFilterData(filterFields []string) *TimeFieldFilter {
	return &TimeFieldFilter{this, filterFields,
		[]string{"login_time", "signin_date"}}
}

func init() {
	// 需要在init中注册定义的model
	orm.RegisterModel(new(User), new(UserExt))
}

func CheckUser(phone string) bool {
	return dataOrm.QueryTable("user").Filter("phone", phone).Exist()
}

func CreateUser(phone, passwd string) (*User, error) {
	user := new(User)
	user.Name = &phone
	user.Phone = &phone
	user.PassWd = &passwd
	user.Sex = Byte(byte(SEX_TYPE_OTHER))
	user.Signature = String("")
	user.NickName = String("")
	user.Updated = time.Now()
	user.ImageId = Int(-1)
	user.UserType = Byte(byte(USER_TYPE_COMMON))
	if _, err := dataOrm.Insert(user); err != nil {
		return nil, err
	}
	userExt := new(UserExt)
	userExt.UserId = user.Id
	userExt.Longitude = Float64(0.0)
	userExt.Latitude = Float64(0.0)
	userExt.LoginTime = time.Now()
	userExt.SigninDays = Int16(int16(0))
	userExt.SigninUnit = Int16(int16(SIGNIN_BASE_UNIT))
	userExt.Integral = Int32(int32(0))
	if _, err := dataOrm.Insert(userExt); err != nil {
		return nil, err
	}
	return user, nil
}

func Login(phone, passwd string) (*User, error) {
	user := new(User)
	qs := dataOrm.QueryTable(user).Filter("phone", phone)
	if err := qs.One(user); err != nil {
		return nil, err
	} else {
		if user.PassWd != nil && passwd != "" &&
			*user.PassWd != passwd {
			return nil, PASSWD_ERR
		}
		userExt := new(UserExt)
		userExt.UserId = user.Id
		userExt.LoginTime = time.Now()
		_, err = dataOrm.Update(userExt, "login_time")
		if err != nil {
			return nil, err
		}
		return user, nil
	}
}

func QuickLogin(phone string) (*User, error) {
	if !CheckUser(phone) {
		return CreateUser(phone, "")
	} else {
		return Login(phone, "")
	}
}

func GetBaseInfo(id int, fields ...string) (*User, error) {
	user := new(User)
	qs := dataOrm.QueryTable(user).Filter("id", id)
	if err := qs.One(user, fields...); err != nil {
		return nil, err
	}
	return user, nil
}

func GetExtInfo(id int, fields ...string) (*UserExt, error) {
	userExt := new(UserExt)
	qs := dataOrm.QueryTable(userExt).Filter("user_id", id)
	if err := qs.One(userExt, fields...); err != nil {
		return nil, err
	}
	return userExt, nil
}

func UpdateInfo(id int, data map[string]interface{}) error {
	qs := dataOrm.QueryTable("user").Filter("id", id)
	if _, err := qs.Update(orm.Params(data)); err != nil {
		return err
	}
	return nil
}

var SigninFields = []string{"signin_date", "signin_days", "signin_unit", "integral"}

func Signin(id int) (*UserExt, error) {
	userExt, err := GetExtInfo(id, SigninFields...)
	if err != nil {
		return nil, err
	}
	if !userExt.SigninDate.IsZero() {
		elapsed := time.Since(userExt.SigninDate)
		if elapsed < time.Hour*24 {
			return nil, errors.New("Today already signed!")
		} else if elapsed < time.Hour*48 {
			(*userExt.SigninDays)++
		} else {
			(*userExt.SigninDays) = 1
		}
	} else {
		(*userExt.SigninDays) = 1
	}
	userExt.SigninDate = time.Now()
	*userExt.Integral += int32(*userExt.SigninUnit)
	if *userExt.SigninDays <= 4 {
		*userExt.SigninUnit = SIGNIN_BASE_UNIT * (*userExt.SigninDays)
	}
	userExt.UserId = id
	if _, err := dataOrm.Update(userExt, SigninFields...); err != nil {
		return nil, err
	}
	return userExt, nil
}
